#include "i2c_controller.h"
#include "../s3c2440_soc.h"


static i2c_msg_p p_cur_msg;

int isLastData(void)
{
    if(p_cur_msg->cnt_transfered == p_cur_msg->len -1)
	return 1;//正要传输最后一个数据
    else
	return 0;
}

void resume_i2c_with_ack(void)
{
    unsigned int iiccon = IICCON;
    iiccon |= (1<<7);
    iiccon &= ~(1<<4);
    IICCON = iiccon;
}

void resume_i2c_without_ack(void)
{
    unsigned int iiccon = IICCON;
    iiccon &= ~((1<<7)|(1<<4));
    IICCON = iiccon;
}

void i2c_interrupt_func(int irq)
{
    printf("i2c_interrupt_func : flags = %d\r\n", p_cur_msg->flags);

    unsigned int i2cstat = IICSTAT;
    p_cur_msg->cnt_transfered++; 
    //每传输完一个数据，将产生一个中断
    //对于每次传输，第一个中断是已经发出地址
    if(p_cur_msg->flags == 0){
	//write
	//第一次中断是发送地址产生的
	//判断是否有ack，如果有ack则表示设备存在，如果没有则
	//表示设备不存在，直接结束
	if(p_cur_msg->cnt_transfered == 0){
	    if(i2cstat & (1<<0)){
		//no ack 
		//停止传输
		IICSTAT = 0xd0;
		IICCON &= ~(1<<4);
		p_cur_msg->err = -1;
		printf("tx err no ack\r\n");
		delay(1000);
		return;
	    }
	}

	if(p_cur_msg->cnt_transfered < p_cur_msg->len){
	    //其他中断则需要继续发送下一个数据
	    IICDS = p_cur_msg->buf[p_cur_msg->cnt_transfered];
	    IICCON &= ~(1<<4);
	}else{
	    //stop transfer	
	    IICSTAT = 0xd0;
	    IICCON &= ~(1<<4);
	    delay(1000);	    
	}
    }else{
	//read 
	//第一个中断，是发送设备地址后产生的，
	//判断是否有ack
	//有ack：设备存在，回复i2c传输，这样在下一个中断才可以得到需要读取的数据
	//无ack：设备不存在，出错，结束传输
	if(p_cur_msg->cnt_transfered == 0){
	    if(i2cstat & (1<<0)){
		//no ack 
		//停止传输
		IICSTAT = 0x90;
		IICCON &= ~(1<<4);
		p_cur_msg->err = -1;
		printf("rx err no ack\r\n");
		delay(1000);
		return;
	    }else{
		//如果是最后一个数据，启动传输时要设置为不回应ack
		if(isLastData){
		    resume_i2c_without_ack();
		}else{
		    resume_i2c_with_ack();		   
		}	

		return;
	    }
	}

	//后续中断，表示得到新数据
	if(p_cur_msg->cnt_transfered < p_cur_msg->len){
	    //从iicds读取保存
	    p_cur_msg->buf[p_cur_msg->cnt_transfered -1] = IICDS;
	    //恢复i2c传输
	    if(isLastData()){
		resume_i2c_without_ack();
	    }else{
		resume_i2c_with_ack();
	    }
	}else{
	    IICSTAT = 0x90;	
	    IICCON &= ~(1<<4);
	    delay(1000);
	}
    }
}

void s3c2440_i2c_ctrl_init(void)
{
    //set i2c gpio
    GPECON &= ~((3<<28) | (3<<30));
    GPECON |= ((2<<28) | (2<<30));

    //set clock
    /* [7] : IIC-bus acknowledge enable bit, 1-enable in rx mode */
    /* [6] : 时钟源，0：I2C Clk = fpclk/16; 1:I2C Clk = fpclk/512; */
    /* [5] : 1-enable interrupt */
    /* [4] : 读出为1时表示发生了中断，写入0来清除并回复I2C操作 */
    /* [3:0] : tx clock = i2c clk/(i2ccon[3:0] + 1) */
    /* tx clock = 100kHz = 50mHz/16/(i2ccon[3:0] + 1) */
    IICCON =  (1<<7) | (0<<6) | (1<<5) | (30<<0);

    //register interface
    register_irq(27,i2c_interrupt_func);
}

int do_master_transmit(i2c_msg_p msg)
{
    p_cur_msg = msg;
    msg->cnt_transfered = -1;
    msg->err = 0;
    //设置寄存器启动传输
    // 1, 配置为master mode 
    IICCON |= (1<<7); //tx mode 在ack周期释放sda
    IICSTAT = (1<<4); 
    // 2，把从设备地址写入IICDS
    IICDS = msg->addr<<1;//地址只有7位，最低以为用来表示读写
    // 3，IICSTAT = 0xf0,数据即被发送出去，将产生中断 
    IICSTAT = 0xf0;
    //后续传输由中断驱动

    //循环等待中断处理完毕
    while(!msg->err && msg->cnt_transfered != msg->len);

    return msg->err;
}

int do_master_receive(i2c_msg_p msg)
{
    p_cur_msg = msg;
    msg->cnt_transfered = -1;
    msg->err = 0;
    //设置寄存器启动传输
    // 1, 配置为master mode 
    IICCON |= (1<<7); //rx mode 回应ack给从设备 
    IICSTAT = (1<<4); 
    // 2，把从设备地址写入IICDS
    IICDS = (msg ->addr<<1) | (1<<0);//地址只有7位，最低以为用来表示读写
    // 3，IICSTAT = 0xf0,数据即被发送出去，将产生中断 
    IICSTAT = 0xb0;
    //后续传输由中断驱动

    //循环等待中断处理完毕
    while(!msg->err && msg->cnt_transfered != msg->len);

    return msg->err;
}


int s3c2440_master_xfer(i2c_msg_p msgs,int num)
{
    int i;
    int err;
    for (i = 0; i < num; ++i) {
	if(msgs[i].flags == 0)
	    err = do_master_transmit(&msgs[i]);
	else
	    err = do_master_receive(&msgs[i]);
	if(err){
	    return err;	
	}
    }
    return 0;
}

static i2c_ctrl_t s3c2440_i2c_ctrl = {
    .name = "s3c2440",
    .init = s3c2440_i2c_ctrl_init,
    .master_xfer = s3c2440_master_xfer,
};

void s3c2440_i2c_ctrl_add()
{
    register_i2c_ctl(&s3c2440_i2c_ctrl);
}
